﻿using iTool.ClusterComponent;
using iToolService.Interfaces;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace iToolService.Implements
{
    public class RequestIdempotenceService : iToolServiceBase, IRequestIdempotenceService
    {
        private Queue<string> taskQueue = new Queue<string>();
        private ConcurrentDictionary<string, TaskInput> taskDictionary = new ConcurrentDictionary<string, TaskInput>();
        private long cacheResultSize { get; set; }

        public override Task OnActivateAsync()
        {
            this.cacheResultSize = this.GetLongKey(out _);
            base.RegisterTimer(x => this.CheckTimeoutOwner(), null, TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10));
            return base.OnActivateAsync();
        }

        public async Task<object> GetResultAsync(string token)
        {
            if (this.taskDictionary.TryGetValue(token,out TaskInput input))
            {
                try
                {
                    return await input.CompletionSource.Task.WaitAsync(input.Timeout);
                }
                catch (Exception)
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }

        public async Task SetResultAsync(string token, object result)
        {
            if (this.taskDictionary.TryGetValue(token, out TaskInput input))
            {
                input.CompletionSource.TrySetResult(result);
            }
            await Task.CompletedTask;
        }

        public Task<bool> StartIfNotExistAsync(string token, int timeout)
        {
            int index = 0; 
            while (this.taskQueue.Count > cacheResultSize & this.taskQueue.TryDequeue(out string needRemoveToken) & index < 5)
            {
                if (this.taskDictionary[needRemoveToken].CompletionSource.Task.IsCompleted)
                {
                    this.taskDictionary.TryRemove(needRemoveToken,out _);
                }
                else
                {
                    this.taskQueue.Enqueue(needRemoveToken);
                }
                index++;
            }

            if (this.taskDictionary.ContainsKey(token))
            {
                return Task.FromResult(false);
            }
            else
            {
                TaskInput input = new TaskInput(timeout);
                this.taskQueue.Enqueue(token);
                this.taskDictionary.TryAdd(token,input);
                return Task.FromResult(true);
            }
        }

        public async Task CheckTimeoutOwner()
        {
            foreach (string token in this.taskQueue)
            {
                if (DateTime.Now - this.taskDictionary[token].Date > TimeSpan.FromSeconds(30))
                {
                    this.taskQueue.Dequeue();
                    this.taskDictionary.TryRemove(token, out _);
                }
                else
                {
                    break;
                }
            }
            await Task.CompletedTask;
        }


    }

    class TaskInput 
    {
        public TaskInput(int timeout) 
        {
            this.CompletionSource = new TaskCompletionSource<object>();
            this.Timeout = TimeSpan.FromSeconds(timeout);
            this.Date = DateTime.Now;
        }

        public TimeSpan Timeout { get; }
        public DateTime Date { get; }
        public TaskCompletionSource<object> CompletionSource { get;}
    }
}
